﻿using SDL2;
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace THZFrm {
    class SDLClass {
        public delegate int GetData(IntPtr data, int nWidth, int nHeight);      //获取待显示的YUV数据

        #region SDL显示相关
        private IntPtr pWindow;
        private IntPtr pRenderer;
        private IntPtr pTexture;            // 用于绘制图像
        private byte[] colorBK = new byte[4] { 255, 255, 255, 255 }; // 底色，RGBA
        private SDL.SDL_Rect rcImage;       // 输入图像的宽高
        private SDL.SDL_Rect rcDisplay;     // 显示区域的宽高
        private SDL.SDL_Rect rcScale;       // 用于缩放输入图像
        #endregion

        public GetData getData = null;
        private bool bEnd = false;

        private readonly object objLock = new object();

        public SDLClass(GroupBox groupBox, int nDelHeight) {

            SetDisplayRect(groupBox, nDelHeight);
            SetImageSize(rcDisplay.w, rcDisplay.h);

            getData = new GetData(updateSDL);

            // NOTE 这里人为设定一个比较大的分辨率，要显示的图像分辨率不能超过它。可以理解为画布的尺寸
            int maxWidth = 4096, maxHeight = 2160;
            pWindow = SDL.SDL_CreateWindow(String.Empty,
                groupBox.Location.X,
                groupBox.Location.Y + nDelHeight,
                maxWidth,
                maxHeight,
                SDL.SDL_WindowFlags.SDL_WINDOW_OPENGL | SDL.SDL_WindowFlags.SDL_WINDOW_BORDERLESS);
            pRenderer = SDL.SDL_CreateRenderer(pWindow, -1, SDL.SDL_RendererFlags.SDL_RENDERER_ACCELERATED);
            pTexture = SDL.SDL_CreateTexture(pRenderer, SDL.SDL_PIXELFORMAT_IYUV, (int)SDL.SDL_TextureAccess.SDL_TEXTUREACCESS_STREAMING, maxWidth, maxHeight);

            SDL.SDL_SysWMinfo info = new SDL.SDL_SysWMinfo();
            SDL.SDL_GetWindowWMInfo(pWindow, ref info);
            IntPtr winHandle = info.info.win.window;
            SetParent(winHandle, groupBox.Handle);
            ShowWindow(winHandle, 1);
        }

        public int updateSDL(IntPtr data, int nW, int nH) {
            if(!bEnd) {
                if(rcImage.w != nW || rcImage.h != nH) {
                    SetImageSize(nW, nH);
                }

                // 先把上一帧的内容清空再绘制下一帧
                SDL.SDL_RenderClear(pRenderer);
                SDL.SDL_SetRenderDrawColor(pRenderer, colorBK[0], colorBK[1], colorBK[2], colorBK[3]);

                SDL.SDL_UpdateTexture(pTexture, ref rcImage, data, rcImage.w);
                SDL.SDL_RenderCopy(pRenderer, pTexture, ref rcImage, ref rcScale);
                SDL.SDL_RenderPresent(pRenderer);
            }
            return 0;
        }

        //图像的显示区域发生变化时，需要更新。更新后是否需要重新刷新图像，待定
        public void SetDisplayRect(GroupBox groupBox, int nDelHeight) {
            lock(objLock) {
                rcDisplay.w = groupBox.Width;
                rcDisplay.h = groupBox.Height - nDelHeight;
            }
            UpdateScaleRect();
        }

        public void SetImageSize(int nImageWidth, int nImageHeight) {
            lock(objLock) {
                rcImage.w = nImageWidth;
                rcImage.h = nImageHeight;
            }
            UpdateScaleRect();
        }

        private void UpdateScaleRect() {
            if(rcImage.w == 0 || rcImage.h == 0) {
                return;
            }
            if((rcImage.w != rcDisplay.w) || (rcImage.h != rcDisplay.h)) {
                //  避免整数除法的影响，a/b > c/d  ==> a*d > b*c
                if(rcDisplay.w * rcImage.h > rcImage.w * rcDisplay.h) {
                    // 左右留空
                    rcScale.h = rcDisplay.h;
                    rcScale.w = (int)((float)rcScale.h / rcImage.h * rcImage.w);
                    rcScale.x = (rcDisplay.w - rcScale.w) / 2;
                    rcScale.y = 0;
                } else {
                    // 上下留空
                    rcScale.w = rcDisplay.w;
                    rcScale.h = (int)((float)rcScale.w / rcImage.w * rcImage.h);
                    rcScale.x = 0;
                    rcScale.y = (rcDisplay.h - rcScale.h) / 2;
                }
            } else {
                rcScale = rcDisplay;
            }
        }

        [DllImport("user32.dll")]
        private static extern IntPtr SetParent(IntPtr child, IntPtr newParent);
        [DllImport("user32.dll")]
        private static extern IntPtr ShowWindow(IntPtr handle, int command);

    }
}
